/*
	UART Layer

	This module defines the Uart layer for both Atmega1280 and Atmega128 MCU's

	Use the function hooks to utilise all the advanved features
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include "globals.h"
#include "layer_uart.h"
#include "buffer.h"
#include "bikirom-ints.h"
#include "string.h"

// ---------------------------------------------------------------------
// Macros
#ifdef atmega1280
#define UART_COUNT	3
#else
#define UART_COUNT	2
#endif
#define UART_TIMEOUT 0x200

// ---------------------------------------------------------------------
// Typedefs
typedef struct _typUart {
	unsigned char	*rxBuf;			/* Pointer to buffer to store Rx Data */
	unsigned char	*rxBufPtr;		/* Write pointer for faster access */
	unsigned short	rxLength;		/* Length of the buffer */
	volatile unsigned short	rxBytes;		/* Bytes in the recieve buffer */
	volatile unsigned short	rxNotify;		/* Number of bytes to notify on */
	_uart_recieve	uart_recieve;	/* Function callback */
	cBuffer			txBuffer;		/* Transmit Buffer */
	enum _eExtInts	eRtsInt;		/* RTS Interrupt */
} _typUartRx;

typedef struct _typUartVars {
	struct _typUart	typUarts[UART_COUNT];	/* Uart Buffers */

} _typUartVars;

typedef struct _typUartRegs {
	volatile unsigned char	*UCSRXA;
	volatile unsigned char	*UCSRXB;
	volatile unsigned char	*UBRRXL;
	volatile unsigned char	*UDRX;
	vpIntHandler	vpRts;
} _typUartRegs;

// ---------------------------------------------------------------------
// Private Functions
void	uart_set_baud		(uint8_t nUart, u32 baudrate);
void	uart0_signal_rts	(unsigned char state);
void	uart1_signal_rts	(unsigned char state);
void	uart2_signal_rts	(unsigned char state);
void	uart3_signal_rts	(unsigned char state);
//__attribute__((naked)) 
__inline__ static void	uart_recieve		(uint8_t nUart, uint8_t c);

// ---------------------------------------------------------------------
// Static Varialbes
struct _typUartVars	typVars;	/* Local Varialbes for Uarts */
static _typUartRegs	typRegs[] =
{
	{&UCSR0A, &UCSR0B, &UBRR0L, &UDR0, &uart0_signal_rts},	
	{&UCSR1A, &UCSR1B, &UBRR1L, &UDR1, &uart1_signal_rts},
#if UART_COUNT > 2
	{&UCSR2A, &UCSR2B, &UBRR2L, &UDR2, &uart2_signal_rts},
#endif
#if UART_COUNT > 3
	{&UCSR3A, &UCSR3B, &UBRR3L, &UDR3, &uart3_signal_rts},
#endif
};

// ---------------------------------------------------------------------
// Functions Bodies
unsigned char	uart_init		(uint8_t nUart, unsigned long nBaud, _uart_recieve uart_recieve,
								 unsigned char* uchTxBuffer, u16 nTxLength,
								 unsigned char* uchRxBuffer, u16 nRxLength,
								 enum _eExtInts eRtsInt)
{
	// Rx Initialisation
	typVars.typUarts[nUart].rxBuf = uchRxBuffer;
	typVars.typUarts[nUart].rxBufPtr = uchRxBuffer;
	typVars.typUarts[nUart].rxLength = nRxLength;
	typVars.typUarts[nUart].rxBytes = 0;
	typVars.typUarts[nUart].rxNotify = 1;
	typVars.typUarts[nUart].uart_recieve = uart_recieve;

	// Tx Initialisation
	typVars.typUarts[nUart].eRtsInt = eRtsInt;
	bufferInit(&typVars.typUarts[nUart].txBuffer, uchTxBuffer, nTxLength);

	// Set the bause
	uart_set_baud(nUart, nBaud);

	// Enable the Tx and Rx
	*typRegs[nUart].UCSRXB |= (unsigned char)(BV(TXEN0) | BV(RXEN0));

	// Enable Interrupts
	sei();
	
	// Rts Handler
	if (eRtsInt != eIntNone)
	{
		// Attach the interrupt
		int_attach(eRtsInt, eIntCtrlAny, typRegs[nUart].vpRts);
	}

	// Enable the Rx
	uart_enable_rx(nUart, 1);
	
	return TRUE;
}

// Enable the Rx
void			uart_enable_rx	(uint8_t nUart, uint8_t bEnable)
{
	if (bEnable)
	{
		*typRegs[nUart].UCSRXB |= (unsigned char)BV(RXCIE0);
	}
	else
	{
		*typRegs[nUart].UCSRXB &= (unsigned char)~(BV(RXCIE0));
	}
}

// Enable the Tx
void			uart_enable_tx	(uint8_t nUart,	uint8_t bEnable)
{
	if (bEnable 
	&& typVars.typUarts[nUart].txBuffer.datalength > 0)
	{
		if (bEnable == 0x02 
		|| typVars.typUarts[nUart].eRtsInt == eIntNone 
		|| int_trigger(typVars.typUarts[nUart].eRtsInt))
		{
			*typRegs[nUart].UCSRXB |= (unsigned char)BV(UDRIE0);
		}
	}
	else
	{
		*typRegs[nUart].UCSRXB &= (unsigned char)~(BV(UDRIE0));
	}
}

// Number of bytes to notify on
void			uart_bytes_rx	(uint8_t nUart,	u16 nBytes)
{
	typVars.typUarts[nUart].rxNotify = nBytes;
}


void	uart_send_byte			(uint8_t nUart, uint8_t data)
{
	// Append to buffer
	bufferAddToEnd(&typVars.typUarts[nUart].txBuffer, data);
	// Enable Tx
	uart_enable_tx(nUart, 1);
}

void	uart_send_byte_unbuffered (uint8_t nUart, uint8_t data)
{
	*typRegs[nUart].UDRX = data;
}

uint8_t		uart_send_buffer		(uint8_t nUart, unsigned char* buffer, u16 nBytes)
{
	bufferAddBufferToEndUnSafe(&typVars.typUarts[nUart].txBuffer, buffer, nBytes);
	// Enable Tx
	uart_enable_tx(nUart, 1);

	return 1;
}

void	uart_append_buffer_tx	(uint8_t nUart, unsigned char* buffer, u16 nBytes)
{
	bufferAddBufferToEndUnSafe(&typVars.typUarts[nUart].txBuffer, buffer, nBytes);
}

void	uart_flush_tx_buffer	(uint8_t nUart)
{
	bufferFlush(&typVars.typUarts[nUart].txBuffer);
	uart_enable_tx(nUart, 0);
}

/* returns -1 for failure */
__inline__ short	uart_get_byte			(uint8_t nUart)
{
	register unsigned short timeout = UART_TIMEOUT;

	do
	{
		if (*typRegs[nUart].UCSRXA & BV(RXC0))
		{
			return *typRegs[nUart].UDRX;
		}
		else
		{
			wdt_reset();
		}	

	} while (--timeout);

	return -1;
}

void	uart_append_buffer_rx	(uint8_t nUart, unsigned char* buffer, u16 nBytes)
{
	if (typVars.typUarts[nUart].rxBytes + nBytes <= typVars.typUarts[nUart].rxLength)
	{
		// Copy into the buffer
		memcpy(&typVars.typUarts[nUart].rxBuf[typVars.typUarts[nUart].rxBytes]
			,buffer, nBytes);

		// Increment size
		typVars.typUarts[nUart].rxBytes += nBytes;

		// Notify listener if required to do so
		if (typVars.typUarts[nUart].rxBytes >= typVars.typUarts[nUart].rxNotify)
		{
			typVars.typUarts[nUart].uart_recieve(
				typVars.typUarts[nUart].rxBuf[typVars.typUarts[nUart].rxBytes]
				, typVars.typUarts[nUart].rxBytes);
		}
	}
}

void	uart_flush_rx_buffer	(uint8_t nUart)
{
	typVars.typUarts[nUart].rxBytes = 0;
	typVars.typUarts[nUart].rxBufPtr = typVars.typUarts[nUart].rxBuf;
}

void uart_set_baud(uint8_t nUart, u32 baudrate)
{
	// calculate division factor for requested baud rate, and set it
	uint8_t baudrateDiv;
	
	baudrateDiv = (uint8_t)((F_CPU+(baudrate*8L))/(baudrate*16L)-1);

	*typRegs[nUart].UBRRXL = baudrateDiv;
}

void	uart_recieve(uint8_t nUart, uint8_t c) 
{
	typVars.typUarts[nUart].rxBuf[typVars.typUarts[nUart].rxBytes++] = c;

	// Notify listener if required to do so
	if (typVars.typUarts[nUart].rxBytes >= typVars.typUarts[nUart].rxNotify)
	{
		typVars.typUarts[nUart].uart_recieve(c, typVars.typUarts[nUart].rxBytes);
	}
}

void	uart0_signal_rts	(unsigned char state)
{
	uart_enable_tx(0, state ? 2 : 0 );
}

SIGNAL(SIG_USART0_DATA)
{
	*typRegs[0].UDRX = bufferGetFromFront(&typVars.typUarts[0].txBuffer);

	uart_enable_tx(0, 2);
}

/*SIGNAL(SIG_USART0_RECV)
{
	register unsigned char c = inb(UDR0);
	//typVars.typUarts[0].rxBufPtr++ = c;
	//typVars.typUarts[0].rxBytes++;
	//typVars.typUarts[0].uart_recieve(c, typVars.typUarts[0].rxBytes);
	typVars.typUarts[0].rxBuf[typVars.typUarts[0].rxBytes++] = c;
	
	// Notify listener if required to do so
	if (typVars.typUarts[0].rxBytes >= typVars.typUarts[0].rxNotify)
	{
		typVars.typUarts[0].uart_recieve(c, typVars.typUarts[0].rxBytes);
	}
}*/

void	uart1_signal_rts	(unsigned char state)
{
	uart_enable_tx(1, state ? 2 : 0 );
}

SIGNAL(SIG_USART1_DATA)
{
	*typRegs[1].UDRX = bufferGetFromFront(&typVars.typUarts[1].txBuffer);

	uart_enable_tx(1, 2);
}

SIGNAL(SIG_USART1_RECV)
{
	uart_recieve(1, inb(UDR1));
}

#if UART_COUNT > 2

void	uart2_signal_rts	(unsigned char state)
{
	uart_enable_tx(2, state ? 2 : 0 );
}

SIGNAL(SIG_USART2_DATA)
{
	*typRegs[2].UDRX = bufferGetFromFront(&typVars.typUarts[2].txBuffer);

	uart_enable_tx(2, 2);
}

SIGNAL(SIG_USART2_RECV)
{
	uart_recieve(2, inb(UDR2));
}

#endif

#if UART_COUNT > 3

void	uart3_signal_rts	(unsigned char state)
{
	uart_enable_tx(3, state ? 2 : 0 );
}

SIGNAL(SIG_USART3_DATA)
{
	*typRegs[3].UDRX = bufferGetFromFront(&typVars.typUarts[3].txBuffer);

	uart_enable_tx(3, 2);
}

SIGNAL(SIG_USART3_RECV)
{
	uart_recieve(3, inb(UDR3));
}

#endif
